package com.njupt.simulation.utils;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.Random;

public class TerminalSimulation {

    // 使用泊松分布公式生成工作终端数
    public static int generatePoissonRandom(double lambdaNormalized, int totalTerminals) {
        Random rand = new Random();

        // 计算实际的 λ
        int lambda = (int) Math.round(lambdaNormalized * totalTerminals);  // 归一化后的 λ 乘以总终端数

        // 设置精度（例如100位精度）
        MathContext mc = new MathContext(100);

        // 计算 e 的近似值
        BigDecimal e = calculateE(mc);

        // 用泊松分布生成一个随机数
        BigDecimal L = e.pow(lambda);  // e^λ
        BigDecimal p, q;
        BigDecimal sum_q = BigDecimal.valueOf(0.0);
        int k = 0;
        BigInteger a = BigInteger.valueOf(1);

        // 生成泊松分布随机数，表示实际工作的终端数量
        do {
            BigDecimal plus = new BigDecimal(lambda).pow(k);  // λ^k
            if (k == 5000){
                System.out.println("test\n");
            }
            if (k > 0){
                a = a.multiply(BigInteger.valueOf(k));  // 阶乘
            }
            q = plus.divide(new BigDecimal(a).multiply(L), 1000, RoundingMode.HALF_UP);  // 用户数为k的概率
            sum_q = sum_q.add(q);
            p = new BigDecimal(1).subtract(sum_q);  // 用户数大于k的概率
            k++;
        } while (p.multiply(generateRandomDecimal(100)).compareTo(q.multiply(generateRandomDecimal(100))) == 1);

        return k;
    }

    public static void main(String[] args) {
        // 定义系统总终端数
        int totalTerminals = 10000;  // 总终端数

        // 设置归一化后的 λ（业务强度），例如设置为 0.2 表示 20% 的强度
        double lambdaNormalized = 0.5;  // 归一化后的业务强度（范围 0 到 1）

        // 调用泊松分布函数，计算实际工作的终端数量
        int activeTerminals = generatePoissonRandom(lambdaNormalized, totalTerminals);

        // 确保实际工作的终端数不会超过总终端数
        activeTerminals = Math.min(activeTerminals, totalTerminals);

        // 计算实际工作的终端比例
        double ratio = (double) activeTerminals / totalTerminals;

        // 输出结果
        System.out.println("总终端数: " + totalTerminals);
        System.out.println("归一化的业务强度 λ: " + lambdaNormalized);
        System.out.println("实际工作的终端数量: " + activeTerminals);
        System.out.println("实际工作的终端数量与总数量的比值: " + ratio);
    }

    // 使用泰勒级数计算 e 的值
    public static BigDecimal calculateE(MathContext mc) {
        BigDecimal sum = BigDecimal.ONE;  // 从 1 开始
        BigDecimal factorial = BigDecimal.ONE;
        BigDecimal term;

        // 计算 e 的近似值，限制计算到 50 项
        for (int i = 1; i < 50; i++) {
            factorial = factorial.multiply(BigDecimal.valueOf(i));  // 计算 i!
            term = BigDecimal.ONE.divide(factorial, mc);  // 计算 1/i!
            sum = sum.add(term);  // 累加每一项
        }

        return sum;
    }

    // 生成一个高精度的随机数，范围在 [0, 1) 之间
    public static BigDecimal generateRandomDecimal(int precision) {
        // 使用 Random 生成 [0, 1) 范围内的随机 double
        Random rand = new Random();
        double randomValue = rand.nextDouble();  // 生成 [0, 1) 范围的随机数

        // 将生成的 double 转换为 BigDecimal，并设置精度
        BigDecimal bigRandom = new BigDecimal(randomValue).setScale(precision, RoundingMode.HALF_UP);

        return bigRandom;
    }
}
